/*
 *    <one line to give the program's name and a brief idea of what it does.>
 *    Copyright (C) 2012  Jeremy W. Murphy <jeremy.william.murphy@gmail.com>
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef CREATE_H
#define CREATE_H

#include "subfactory.h"
#include "function.h"
#include "terminal.h"

#include <random>
#include <stdexcept>
#include <functional>
#include <memory>


namespace rmitgprime
{
	template <typename T, typename... TerminalParameters, unsigned N, typename... FunctionParameters, typename ReturnType = double, typename Engine = std::mt19937>
    std::unique_ptr<Node<ReturnType>> full_new(unsigned const &DEPTH,
											   std::uniform_int_distribution<std::size_t> const &func_dist,
											   std::uniform_int_distribution<std::size_t> const &term_dist,
											   Engine &engine,
											   Factory<Terminal<T>, TerminalParameters...> const &TERMINAL_FACTORY,
											   Factory<Function<T, N>, FunctionParameters...> const &FUNCTION_FACTORY)
    {
        auto func_index = std::bind(func_dist, engine), term_index = std::bind(term_dist, engine);
        std::unique_ptr<Node<ReturnType>> node;

        if(DEPTH == 0)
        {
            auto ReturnType_generator = std::bind(std::uniform_real_distribution<ReturnType>(0, 1), engine);
			node = TERMINAL_FACTORY.spawn(term_index(), ReturnType_generator());
        }
        else
        {
            node = FUNCTION_FACTORY.spawn(func_index());
            // TODO: Create children
        }

        if(node == nullptr)
            throw new std::runtime_error("full_new(): Failed to create node.");

        return node;
    }
}

#endif  // CREATE_H
